package com.dhcc.bpm.alarm.service.impl;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dhcc.bpm.alarm.entity.*;
import com.dhcc.bpm.alarm.service.*;
import com.dhcc.bpm.alarm.utils.SQLutil;
import com.dhcc.bpm.alarm.utils.SSHExecutor;
import com.dhcc.bpm.alarm.utils.SftpUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.script.*;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Slf4j
@Service
public class WarningExecutionServiceImpl implements WarningExecutionService {
    @Autowired
    private IAlarmStrategyInfoService alarmStrategyInfoService;
    @Autowired
    private IAlarmNoticeInfoService alarmNoticeInfoService;
    @Autowired
    private IAlarmNoticeTargetService alarmNoticeTargetService;
    @Autowired
    private IAlarmRecordService alarmRecordService;
    @Autowired
    private IAlarmDbInfoConfigService alarmDbInfoConfigService;
    @Autowired
    private IAlarmFtpInfoConfigService alarmFtpInfoConfigService;
    private ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()/2);
    @Override
    public void exec(String batchTaskNo) throws Exception{
        List<AlarmStrategy> alarmStrategyList = alarmStrategyInfoService.list(new QueryWrapper<AlarmStrategy>().eq("BATCH_TASK_NO",batchTaskNo));
        if(alarmStrategyList.isEmpty()){
            throw new Exception("没有可用策略，执行失败");
        }
        for (AlarmStrategy alarmStrategy:alarmStrategyList) {
            AlarmNotice alarmNotice = alarmNoticeInfoService.getOne(new QueryWrapper<AlarmNotice>().in("STRATEGY_CODE",alarmStrategy.getStrategyCode()).eq("ALARM_STATUS", "1"));
            String execMethod = alarmStrategy.getExecMethod();
            if(alarmNotice==null){
                log.error("告警对象通知不存在 STRATEGY_CODE:{}",alarmStrategy.getStrategyCode());
            	continue; 
            }
            List<Long> objectList = alarmNoticeTargetService.list(new QueryWrapper<AlarmNoticeTarget>().eq("NOTICE_ID",alarmNotice.getId()))
                    .stream().map(AlarmNoticeTarget::getTargetId).collect(Collectors.toList());
            if(objectList.isEmpty()){
            	log.error("没有操作对象, NOTICE_ID:{}", alarmNotice.getId());
                continue;
            }
            if("01".equals(execMethod)){
                log.info("服务器告警");
                List<AlarmFtpInfoConfig> alarmFtpInfoConfigList = alarmFtpInfoConfigService.list(new QueryWrapper<AlarmFtpInfoConfig>().in("ID",objectList).eq("IENABLE","1"));
                for(int i = 0 ; i<alarmFtpInfoConfigList.size();i++){
                    final AlarmFtpInfoConfig alarmFtpInfoConfig = alarmFtpInfoConfigList.get(i);

                    fixedThreadPool.execute(new Runnable() {
                        @SneakyThrows
                        @Override
                        public void run() {
                            SSHExecutor ssh = new SSHExecutor();
                            JSONObject session =  ssh.getSSHSession(alarmFtpInfoConfig);
                            try {
                                JSONObject message = ssh.exec(session,"mkdir .alarmStrategy");
                                RandomAccessFile randomAccessFile = null;
                                String fileName = alarmStrategy.getStrategyCode()+"-"+ UUID.randomUUID().toString()+".sh";

                                randomAccessFile = new RandomAccessFile(fileName, "rw");
                                randomAccessFile.writeBytes(alarmStrategy.getRuleScript());
                                if(randomAccessFile!=null){
                                    try {
                                        randomAccessFile.close();
                                    }catch (Exception e){
                                        e.printStackTrace();
                                    }
                                }
                                SftpUtils.sftpUpload(session,".",".alarmStrategy",fileName,fileName,false);
                                File file = new File(fileName);
                                FileUtil.del(file);
                                message = ssh.exec(session,"sh .alarmStrategy/"+fileName);
                                ssh.exec(session,"rm -rf .alarmStrategy/"+fileName);
                                String msg = message.getString("message");
                                if(!message.getBoolean("code")){
                                    log.error("命令执行失败:{}",msg);
                                    return;
                                }
                                if(!StringUtils.isBlank(msg)){
                                    //执行告警流程
                                    msg = msg.replaceAll("\r|\n", "");
                                    log.info("脚本输出告警信息内容{}",msg);
                                }else {
                                    log.info("没有告警信息");
                                    return;
                                }
                                //然后判断他是告警方式是
                                //1、脚本告警
                                if("02".equals(alarmStrategy.getContentType())){
                                    alarmRecordService.insertAlarmRecord(null,alarmStrategy,alarmNotice,msg,"02",alarmFtpInfoConfig);
                                }else if("01".equals(alarmStrategy.getContentType())){
                                    //判断是否到达阈值
                                    Compilable compilable = (Compilable) engine;
                                    String script = "F"+alarmStrategy.getAlarmTerm()+"D"; //定义函数并调用
                                    CompiledScript JSFunction = null; //解析编译脚本函数
                                    try {
                                        Bindings bindings = engine.createBindings(); //Local级别的Binding
                                        JSFunction = compilable.compile(script);
                                        bindings.put("F", msg);
                                        bindings.put("D", alarmStrategy.getAlarmThreshold());
                                        Object result = JSFunction.eval(bindings);
                                        if((Boolean) result){
                                            //执行告警流程
                                            alarmRecordService.insertAlarmRecord(null,alarmStrategy,alarmNotice,msg,"02",alarmFtpInfoConfig);
                                        }
                                    } catch (ScriptException e) {
                                        log.error("判断是否达到阈值错误",e);
                                    }
                                }
                            }catch (Exception e){
                                log.error("告警执行失败:",e);
                            }finally {
                                ssh.close(session);
                            }
                        }
                    });
                }
            }else if("02".equals(execMethod)){
                log.info("数据库告警");
                List<AlarmDbInfoConfig> alarmDbInfoConfigList = alarmDbInfoConfigService.list(new QueryWrapper<AlarmDbInfoConfig>().in("ID",objectList).eq("IENABLE","1"));
                for(int i = 0 ; i<alarmDbInfoConfigList.size();i++){
                    final AlarmDbInfoConfig alarmDbInfoConfig = alarmDbInfoConfigList.get(i);
                    fixedThreadPool.execute(new Runnable() {
                        @SneakyThrows
                        @Override
                        public void run() {
                            JdbcTemplate jdbcTemplate = SQLutil.getJdbcTemplate(alarmDbInfoConfig);
                            log.info("执行的sql命令{}",alarmStrategy.getRuleScript());
                            List<Map<String,Object>> data = jdbcTemplate.queryForList(alarmStrategy.getRuleScript());
                            log.info("执行的sql命令结果{}",data);
                            SQLutil.close(jdbcTemplate);
                            
                            if(data.size() > 5) {
                            	log.info("结果大于5条不执行！");
                            	return;
                            }
                            
                            //然后判断他是告警方式是
                            //1、脚本告警
                            if("02".equals(alarmStrategy.getContentType())){
                                if(!data.isEmpty()){
                                    //执行告警流程
                                    Object msg = JSON.toJSON(data);
                                    alarmRecordService.insertAlarmRecord(null,alarmStrategy,alarmNotice,msg.toString(),"02",alarmDbInfoConfig);
                                }
                            }else if("01".equals(alarmStrategy.getContentType())){
                                //判断是否到达阈值
                                Compilable compilable = (Compilable) engine;
                                String script = "F"+alarmStrategy.getAlarmTerm()+"D"; //定义函数并调用
                                CompiledScript JSFunction = null; //解析编译脚本函数
                                try {
                                    Map<String,Object> map  = data.get(0);
                                    Bindings bindings = engine.createBindings(); //Local级别的Binding
                                    JSFunction = compilable.compile(script);
                                    bindings.put("F", map.get("result").toString());
                                    bindings.put("D", alarmStrategy.getAlarmThreshold());
                                    Object result = JSFunction.eval(bindings);
                                    if((Boolean) result){
                                        //执行告警流程
                                        alarmRecordService.insertAlarmRecord(null,alarmStrategy,alarmNotice,map.get("result").toString(),"02",alarmDbInfoConfig);
                                    }
                                } catch (ScriptException e) {
                                    log.error("判断是否达到阈值错误",e);
                                }
                            }
                        }
                    });
                }

            }
        }
    }

}
